package com.lhkj.ct.meta.modules.patient.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.lhkj.ct.base.constants.DictConstants;
import com.lhkj.ct.base.enums.DeleteStatus;
import com.lhkj.ct.base.enums.GlobalStatus;
import com.lhkj.ct.base.enums.PatSourceEnum;
import com.lhkj.ct.base.mybatis.core.metadata.SqlOrder;
import com.lhkj.ct.base.mybatis.extension.service.impl.BatchHandleServiceImpl;
import com.lhkj.ct.base.utils.Asserts;
import com.lhkj.ct.base.utils.ShiroUtil;
import com.lhkj.ct.base.utils.TreeUtil;
import com.lhkj.ct.base.utils.Validate;
import com.lhkj.ct.meta.admin.service.SysDictDataService;
import com.lhkj.ct.meta.modules.patient.converter.PFormConverter;
import com.lhkj.ct.meta.modules.patient.mapper.PatUnitMapper;
import com.lhkj.ct.meta.modules.patient.mapper.PatientMapper;
import com.lhkj.ct.meta.modules.patient.model.dto.ArchiveConsolidationDto;
import com.lhkj.ct.meta.modules.patient.model.dto.PatBasicInfo;
import com.lhkj.ct.meta.modules.patient.model.dto.PatientQuery;
import com.lhkj.ct.meta.modules.patient.model.entity.TblPatCrowdTag;
import com.lhkj.ct.meta.modules.patient.model.entity.TblPatSource;
import com.lhkj.ct.meta.modules.patient.model.entity.TblPatUnit;
import com.lhkj.ct.meta.modules.patient.model.entity.TblPatient;
import com.lhkj.ct.meta.modules.patient.model.vo.PatientBasicVo;
import com.lhkj.ct.meta.modules.patient.model.vo.PatientForm;
import com.lhkj.ct.meta.modules.patient.model.vo.PatientPageVo;
import com.lhkj.ct.meta.modules.patient.service.PatSourceService;
import com.lhkj.ct.meta.modules.patient.service.PatientService;
import com.lhkj.ct.meta.modules.psychology.model.bo.SubmitGaugeBo;
import com.lhkj.ct.meta.modules.recycle.common.constant.RecycleType;
import com.lhkj.ct.meta.modules.recycle.entity.TblRecycleBin;
import com.lhkj.ct.meta.modules.recycle.service.RecycleBinService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 档案表 服务实现类
 * </p>
 *
 * @author jobob
 * @since 2023-09-17
 */
@Service
public class PatientServiceImpl extends BatchHandleServiceImpl<PatientMapper, TblPatient> implements PatientService {

    @Resource
    private SysDictDataService sysDictDataService;

    @Resource
    private RecycleBinService recycleBinService;

    @Resource
    private PatSourceService patSourceService;

    @Resource
    private PatUnitMapper patUnitMapper;

    @Override
    public IPage<PatientPageVo> getUserPage(PatientQuery patientQuery) {
        return getBaseMapper().getUserPage(generatePage(patientQuery, SqlOrder.desc(TblPatient::getCreateTime, "t")), patientQuery);
    }

    @Override
    public IPage<PatientBasicVo> getBasicUserPage(PatientQuery patientQuery) {
        // 如果没有指定排序参数，则按照创建日期降序排列
        if (!StringUtils.isNotBlank(patientQuery.getIsAsc())) {
            patientQuery.setIsAsc("descending");
            patientQuery.setOrderByColumn("createTime");
        }
        return getBaseMapper().getBasicUserPage(generatePage(patientQuery), patientQuery);
    }

    @Override
    public List<TblPatUnit> selectPatUnitTreeWithCount(TblPatUnit patUnit) {
        List<TblPatUnit> tblPatUnits = getBaseMapper().selectTreeWithPatCount(patUnit);
        return TreeUtil.builderTree(tblPatUnits);
    }

    @Override
    public List<TblPatCrowdTag> selectPatTagWithCount(TblPatCrowdTag tag) {
        return getBaseMapper().selectPatTagWithCount(tag);
    }

    @Override
    public PatientForm getForm(String id) {
        LambdaQueryWrapper<TblPatient> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .select(TblPatient::getId,
                        TblPatient::getPatAvatar,
                        TblPatient::getPatName,
                        TblPatient::getPatSex,
                        TblPatient::getPatIdentType,
                        TblPatient::getPatIdentNo,
                        TblPatient::getPatBirthday,
                        TblPatient::getPatNationality,
                        TblPatient::getPatNation,
                        TblPatient::getPatNativePlace,
                        TblPatient::getPatAddress,
                        TblPatient::getPatEmail,
                        TblPatient::getPatMobile,
                        TblPatient::getPatMarriage,
                        TblPatient::getPatJob,
                        TblPatient::getPatEducation,
                        TblPatient::getPatUnit,
                        TblPatient::getPatOrgan,
                        TblPatient::getPatCardNo,
                        TblPatient::getPatInNo);
        queryWrapper.eq(TblPatient::getId, id);
        return getBaseMapper().getForm(queryWrapper);
    }

    @Override
    public int insert(TblPatient entity) {
        if (entity.getStatus() == null) {
            entity.setStatus(GlobalStatus.NORMAL);
        }
        return getBaseMapper().insert(entity);
    }

    @Override
    public int savePatient(PatientForm patientForm) {
        TblPatient patient = PFormConverter.INSTANCE.convertForm2Patient(patientForm);
        // 单机构判断证件号码是否已存在，如多机构，可自行修改判断逻辑
        Asserts.assertTrue(checkIdentNoUnique(patient), MessageFormat.format("证件号码[{0}]已存在，请勿重复建档", patientForm.getPatIdentNo()));
        // 校验字典合法性
        validatedDict(patient);
        boolean insert = StringUtils.isBlank(patientForm.getId());
        if (insert) {
            patient.setCreateBy(ShiroUtil.getAuthUserInfo().getUserId());
        }else{
            patient.setUpdateBy(ShiroUtil.getAuthUserInfo().getUserId());
        }
        int res = insert ? getBaseMapper().insert(patient) : getBaseMapper().updateById(patient);
        if (StringUtils.isBlank(patientForm.getId())){
            patSourceService.insertOnDuplicate(new TblPatSource(patient.getId(), PatSourceEnum.LR, ShiroUtil.getAuthUserInfo().getUserId()));
        }
        return res;
    }

    /**
     * fixme 后续需要联合回收站进行判断
     */
    @Override
    public boolean checkIdentNoUnique(TblPatient patient) {
        TblPatient target = getBaseMapper().selectOne(
                new LambdaQueryWrapper<TblPatient>()
                        .select(TblPatient::getId)
                        .eq(TblPatient::getPatIdentNo, patient.getPatIdentNo())
        );
        return null == target || StringUtils.equals(target.getId(), patient.getId());
    }


    @Override
    public SubmitGaugeBo.Patient getSubmitUserInfo(String patId) {
        return getBaseMapper().getSubmitUserInfo(patId);
    }

    @Override
    public TblPatient selectByNo(TblPatient patient) {
        LambdaQueryWrapper<TblPatient> wrapper = new LambdaQueryWrapper<TblPatient>()
                // 证件号
                .eq(StringUtils.isNotBlank(patient.getPatIdentNo()),TblPatient::getPatIdentNo, patient.getPatIdentNo())
                // 就诊卡
                .or(StringUtils.isNotBlank(patient.getPatCardNo()),
                        w -> w.eq(TblPatient::getPatCardNo, patient.getPatCardNo()))
                // 住院号
                .or(StringUtils.isNotBlank(patient.getPatInNo()),
                        w -> w.eq(TblPatient::getPatInNo, patient.getPatInNo()));
        if (wrapper.isEmptyOfWhere()) {
            return null;
        }
        wrapper.last(" limit 1");
        return getBaseMapper().selectOne(wrapper);
    }

    @Override
    @SuppressWarnings("unchecked")
    public int removeByIds(Collection<? extends Serializable> idList) {
        return recycleBinService.logicDelete((List<String>)idList, this::logicDelete);
    }

    @Override
    @Transactional
    public int logicDelete(String id) {
        TblPatient target = getBaseMapper().selectOne(
                new LambdaQueryWrapper<TblPatient>()
                .eq(TblPatient::getId, id)
                        .eq(TblPatient::getDelFlag, DeleteStatus.NORMAL)
        );
        if (null == target) {
            return 0;
        }
        return recycleBinService.logicDeleteRealSource(
                getBaseMapper(),
                id,
                TblPatient::getId,
                TblPatient::getDelFlag,
                DeleteStatus.DELETED,
                TblRecycleBin.builder().sourceId(id).title(target.getPatName()).sourceType(RecycleType.PATIENT).build());
    }

    @Override
    public boolean updateUserAvatar(String userId, String avatarUrl) {
        TblPatient user = new TblPatient();
        user.setId(userId);
        user.setPatAvatar(avatarUrl);
        return getBaseMapper().updateById(user) > 0;
    }

    @Override
    public PatBasicInfo getBasicInfo() {
        return getBaseMapper().getBasicInfo(ShiroUtil.getAuthUserInfo().getUserId());
    }

    @Override
    public boolean updateBasicInfo(PatBasicInfo info) {
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.GENDER, info.getPatSex()), "性别不合法");
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.IDENT_TYPE, info.getPatIdentType()), "证件类型不合法");
        LambdaUpdateWrapper<TblPatient> updateWrapper = new LambdaUpdateWrapper<TblPatient>()
                .set(TblPatient::getPatSex, info.getPatSex())
                .set(TblPatient::getPatBirthday, info.getPatBirthday())
                .set(TblPatient::getPatIdentType, info.getPatIdentType())
                .set(TblPatient::getPatIdentNo, info.getPatIdentNo())
                .set(TblPatient::getPatEmail, info.getPatEmail())
                .set(TblPatient::getPatMobile, info.getPatMobile())
                .set(TblPatient::getPatAddress, info.getPatAddress())
                .eq(TblPatient::getId, ShiroUtil.getAuthUserInfo().getUserId());
        return SqlHelper.retBool(getBaseMapper().update(null, updateWrapper));
    }

    @Override
    @Transactional
    public void batchCreate(List<TblPatient> patients) {
        String userId = ShiroUtil.getAuthUserInfo().getUserId();
        // 插入档案数据
        String sqlStatement = "com.lhkj.ct.meta.modules.patient.mapper.PatientMapper.insertOnDuplicate";
        executeBatch(patients, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
        List<TblPatSource> sources = patients.stream().map(p ->
                new TblPatSource(p.getId(), PatSourceEnum.LR, userId)
        ).collect(Collectors.toList());
        // 插入档案来源
        patSourceService.saveBatch(sources);
    }

    @Override
    public boolean consolidation(ArchiveConsolidationDto dto) {
        TblPatUnit tblPatUnit = patUnitMapper.selectById(dto.getUnitId());
        return SqlHelper.retBool(getBaseMapper().update(
                new LambdaUpdateWrapper<TblPatient>()
                        .set(TblPatient::getPatUnit, Validate.notNull(tblPatUnit, "单位不存在").getId())
                        .in(TblPatient::getId, dto.getPatIds())));
    }

    /**
     * 校验字典类型值合法性
     * @param patient
     */
    private void validatedDict(TblPatient patient) {
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.GENDER, patient.getPatSex()), "性别不合法");
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.IDENT_TYPE, patient.getPatIdentType()), "证件类型不合法");
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.NATION, patient.getPatNation()), "民族不合法");
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.EDUCATION, patient.getPatEducation()), "学历不合法");
        Asserts.assertTrue(sysDictDataService.validated(DictConstants.MARRIAGE, patient.getPatMarriage()), "婚姻状况不合法");
    }
}
